﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace UIDemo
{
    /// <summary>
    /// TreeView控件鼠标拖拽实现类
    /// </summary>
    public class TreeViewDragger
    {
        /// <summary>
        /// 指定的节点是否可以拖拽
        /// </summary>
        public Func<TreeNode, bool> Func_CanDrag;

        /// <summary>
        /// 
        /// </summary>
        public Func<TreeNode, TreeNode, bool, bool> Func_DoDrag;

        private const int LineW = 3;
        private bool _enable = true;
        private int _preKeyState = 0;
        private Rectangle _preRect = Rectangle.Empty;
        private Graphics dg;
        private Pen pen = new Pen(Color.Red, LineW)
        {
            DashStyle = DashStyle.Solid,
            StartCap = LineCap.Triangle,
        };

        private Pen pen2 = new Pen(Color.Green, LineW);
        public bool Enable
        {
            get { return _enable; }
            set
            {
                _enable = value;
                HookEvent(_enable);
            }
        }

        public bool EnableEscCancelDrag { get; set; }
        private TreeView _treeView { get; set; }
        public TreeViewDragger(TreeView view)
        {
            _treeView = view;
            dg = _treeView.CreateGraphics();
            EnableEscCancelDrag = true;
            HookEvent(_enable);
        }

        private void HookEvent(bool v)
        {
            this._treeView.AllowDrop = v;
            this._treeView.DragDrop -= TreeView1_DragDrop;
            this._treeView.DragEnter -= TreeView1_DragEnter;
            this._treeView.ItemDrag -= TreeView1_ItemDrag;
            this._treeView.DragLeave -= TreeView1_DragLeave;
            this._treeView.DragOver -= TreeView1_DragOver;
            this._treeView.QueryContinueDrag -= TreeView1_QueryContinueDrag;
            if (v)
            {
                this._treeView.DragDrop += TreeView1_DragDrop;
                this._treeView.DragEnter += TreeView1_DragEnter;
                this._treeView.ItemDrag += TreeView1_ItemDrag;
                this._treeView.DragLeave += TreeView1_DragLeave;
                this._treeView.DragOver += TreeView1_DragOver;
                this._treeView.QueryContinueDrag += TreeView1_QueryContinueDrag;
            }
        }

        private void InitCap()
        {
            GraphicsPath hPath = new GraphicsPath();

            // Create the outline for our custom end cap.
            hPath.AddLine(new Point(-1, -1), new Point(0, 0));
            hPath.AddLine(new Point(0, 0), new Point(-1, 1));
            //hPath.AddLine(new Point(3, 1), new Point(3, 1));

            // Construct the hook-shaped end cap.
            CustomLineCap HookCap = new CustomLineCap(null, hPath);
            HookCap.SetStrokeCaps(LineCap.Round, LineCap.Round);

            //pen.CustomStartCap = HookCap;
            //pen.CustomEndCap = HookCap;

            LineCap scap, ecap;
            HookCap.GetStrokeCaps(out scap, out ecap);

            pen2.StartCap = scap;
            pen2.EndCap = ecap;

        }

        #region Drag Event

        private bool _isDragToSubNode = false;
        private void TreeView1_DragDrop(object sender, DragEventArgs e)
        {
            if (Func_DoDrag == null)
            {
                this._treeView.Refresh();
                return;
            }
            var dnode = e.Data.GetData("drag_target") as TreeNode;
            var snode = e.Data.GetData("System.Windows.Forms.TreeNode") as TreeNode;
            if (dnode == snode)
                return;
            var b = _isDragToSubNode;
            if (Func_DoDrag(snode, dnode, b))
            {
                if (snode.Parent != null)
                {
                    snode.Parent.Nodes.Remove(snode);
                }
                else
                {
                    this._treeView.Nodes.Remove(snode);
                }
                if (b)
                {
                    dnode.Nodes.Insert(0, snode);
                    dnode.Expand();
                }
                else
                {
                    if (dnode.Parent == null)
                    {
                        this._treeView.Nodes.Insert(this._treeView.Nodes.IndexOf(dnode) + 1, snode);
                        //this._treeView.ExpandAll();
                    }
                    else
                    {
                        dnode.Parent.Nodes.Insert(dnode.Parent.Nodes.IndexOf(dnode) + 1, snode);
                        dnode.Parent.Expand();
                    }
                }
            }
            this._treeView.Refresh();
            this._treeView.SelectedNode = snode;
        }

        private void TreeView1_DragEnter(object sender, DragEventArgs e)
        {
            Console.WriteLine("TreeView1_DragEnter");
            if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode"))
            {
                e.Effect = DragDropEffects.Move;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void TreeView1_DragLeave(object sender, EventArgs e)
        {
            Console.WriteLine("TreeView1_DragLeave");
            this._treeView.Refresh();
        }

        private void TreeView1_DragOver(object sender, DragEventArgs e)
        {
            var p = this._treeView.PointToClient(new Point(e.X, e.Y));
            var node = this._treeView.GetNodeAt(p);
            if (node == null)
            {
                return;
            }
            var rect = node.Bounds;
            e.Data.SetData("drag_target", node);
            var p1 = rect.Location;
            var p2 = rect.Location;
            p1.Offset(0, rect.Height);
            p2.Offset(_treeView.Width, rect.Height);
            //清除上一次绘制的区域
            if (!_preRect.IsEmpty && !_preRect.Equals(rect))
            {
                var nRect = new Rectangle(0, _preRect.Y, _treeView.Width, _preRect.Height);
                this._treeView.Invalidate(nRect);
                _preKeyState = -1;
            }
            _preRect = rect;
            p1.Offset(1, -LineW);
            p2.Offset(-1, -LineW);
            p1.Offset(_treeView.Indent, 0);
            if (e.KeyState != _preKeyState)
            {
                //按下Shift键,从根开始绘制
                //if ((e.KeyState & 4) == 4)
                //{
                //    p1.Offset(-p1.X + LineW / 2 + 1, 0);
                //}
                //按下Ctrl键
                Pen pl = pen;
                if ((e.KeyState & 8) == 8)
                {
                    pl = pen2;
                    //p2.Offset(rect.X + _treeView.Width - (LineW / 2 + 1) - p2.X, 0);
                }
                //清空当前绘图区域
                using (var penx = new Pen(_treeView.BackColor, LineW))
                {
                    dg.DrawLine(penx, new Point(0, p1.Y), new Point(_treeView.Width, p1.Y));
                }
                //按下ALT键
                if ((e.KeyState & 32) == 32)
                {
                    p1.X = 0;
                    p1.Offset(2, 0);
                    _isDragToSubNode = false;
                }
                else
                {
                    _isDragToSubNode = true;
                }
                dg.DrawLine(pl, p1, p2);
                _preKeyState = e.KeyState;
            }
        }

        /// <summary>
        /// 开始拖拽
        /// 判断当前的节点是否可以拖拽
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TreeView1_ItemDrag(object sender, ItemDragEventArgs e)
        {
            var node = e.Item as TreeNode;
            if (e.Button != MouseButtons.Left)
                return;
            if (Func_CanDrag != null)
            {
                if (Func_CanDrag.Invoke(node))
                {
                    this._treeView.DoDragDrop(node, DragDropEffects.Move);
                }
            }
        }

        /// <summary>
        /// 判断是否取消拖拽
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TreeView1_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
        {
            if (e.EscapePressed && EnableEscCancelDrag)
                e.Action = DragAction.Cancel;
        }

        #endregion
    }
}